package com.nirvana.flatbuffers;


import com.nirvana.convert.CamelCaseConverter;
import com.nirvana.factory.FlatBuffersResolveFactory;
import org.apache.commons.lang3.StringUtils;

import java.util.regex.Matcher;

import static com.nirvana.config.PluginConfig.collectionPattern;

/**
 * Copyright (c) 2022 Aoshu.Co.Ltd. All rights reserved.
 *
 * @author :    zhouqichun
 * @create :    2025/11/13 14:55
 * @description :
 **/
public class CollectionResolver implements IResolve {
    @Override
    public boolean canResolve(String propertyType) {
        return collectionPattern.matcher(propertyType).matches();
    }

    @Override
    public String resolve(StringBuilder objectPreContent, String defName, String propertyName, String propertyType) {
        String offset = "";
        Matcher matcher = collectionPattern.matcher(propertyType);
        if (!matcher.find()) return offset;

        String type = matcher.group(1);
        String subType = matcher.group(2);
        switch (type) {
            case "Array", "List" -> {
                generateVector(objectPreContent, defName, type, subType, propertyName);
                offset = "%sOffset".formatted(propertyName);
            }
        }
        return offset;
    }

    private void generateVector(StringBuilder objectPreContent, String defName, String type, String subType, String propertyName) {
        IResolve resolver = FlatBuffersResolveFactory.getResolver(subType);
        if (resolver == null) return;

        String offsets = resolver.resolveVector(objectPreContent, defName, propertyName, subType);
        if (StringUtils.isBlank(offsets)) return;

        String toArray = "";
        if (!"Array".equals(type)) toArray = ".toArray";

        objectPreContent.append("""
                                val %sOffset = %sFb.create%sVector(builder, %s%s)
                """.formatted(propertyName, defName, CamelCaseConverter.toCamelCase(propertyName, false), offsets, toArray));
    }

    @Override
    public String resolveVector(StringBuilder objectPreContent, String defName, String propertyName, String propertyType) {
        return "";
    }

    @Override
    public void resolveBaseField(StringBuilder fieldContent, String defName, String comment, String propertyName, String propertyType) {
        Matcher matcher = collectionPattern.matcher(propertyType);
        if (!matcher.find()) return;

        String type = matcher.group(1);
        String subType = matcher.group(2);
        String subTypeParam = subType;
        if (subType.contains("(")) subTypeParam = "(" + subType + ")";

        switch (type) {
            case "Array", "List" -> fieldContent.append("""
                def %s_foreach(action: %s => Unit): Unit
                def %s_filter(action: %s => Boolean): Seq[%s]
                def %s_exists(action: %s => Boolean): Boolean
                def %s_find(action: %s => Boolean): Option[%s]
                def %s_map[T](action: %s => T): Seq[T]
                def %s_take(n: Int): %s
                def %s_drop(n: Int): %s
                def %s_slice(from: Int, until: Int): %s
            """.formatted(
                    // foreach
                    propertyName,
                    subTypeParam,

                    // filter
                    propertyName,
                    subTypeParam,
                    subType,

                    // exists
                    propertyName,
                    subTypeParam,

                    // find
                    propertyName,
                    subTypeParam,
                    subType,

                    // map
                    propertyName,
                    subTypeParam,

                    // take
                    propertyName,
                    propertyType,

                    // drop
                    propertyName,
                    propertyType,

                    // slice
                    propertyName,
                    propertyType
            ));
        }
    }

    @Override
    public void resolveFbWrapperField(StringBuilder fieldContent, String defName, String comment, String propertyName, String propertyType) {
        Matcher matcher = collectionPattern.matcher(propertyType);
        if (!matcher.find()) return;

        String type = matcher.group(1);
        String subType = matcher.group(2);
        String subTypeParam = subType;
        if (subType.contains("(")) subTypeParam = "(" + subType + ")";

        switch (type) {
            case "Array", "List" -> fieldContent.append("""
                                def %s: %s = {
                                    val n = raw.%sLength
                                    if (n == 0) %s.empty
                                    else %s.tabulate(n) { i => raw.%s(i) }
                                }
                            """.formatted(propertyName, propertyType, propertyName, type, type, propertyName));
        }

        fieldContent.append("""
                        def %s_foreach(action: %s => Unit): Unit = {
                            val n = raw.%sLength
                            var i = 0
                            while (i < n) {
                                val t = raw.%s(i)
                                action(t)
                                i += 1
                            }
                        }
                    """.formatted(propertyName, subTypeParam, propertyName, propertyName));

        fieldContent.append("""
                        def %s_filter(action: %s => Boolean): Seq[%s] = {
                            val n = raw.%sLength
                            var i = 0
                            val buf = ArrayBuffer.empty[%s]
                            while (i < n) {
                                val t = raw.%s(i)
                                if (action(t)) {
                                    buf += t
                                }
                                i += 1
                            }
                            buf
                        }
                    """.formatted(propertyName, subTypeParam, subType, propertyName, subType, propertyName));

        fieldContent.append("""
                        def %s_exists(action: %s => Boolean): Boolean = {
                            val n = raw.%sLength
                            var i = 0
                            while (i < n) {
                                val t = raw.%s(i)
                                if (action(t)) return true
                                i += 1
                            }
                            false
                        }
                    """.formatted(propertyName, subTypeParam, propertyName, propertyName));

        fieldContent.append("""
                        def %s_find(action: %s => Boolean): Option[%s] = {
                            val n = raw.%sLength
                            var i = 0
                            while (i < n) {
                                val t = raw.%s(i)
                                if (action(t)) return Some(t)
                                i += 1
                            }
                            None
                        }
                    """.formatted(propertyName, subTypeParam, subType, propertyName, propertyName));

        fieldContent.append("""
                    def %s_map[T](action: %s => T): Seq[T] = {
                        val n = raw.%sLength
                        var i = 0
                        val buf = ArrayBuffer.empty[T]
                        while (i < n) {
                            val t = raw.%s(i)
                            buf += action(t)
                            i += 1
                        }
                        buf
                    }
                """.formatted(propertyName, subTypeParam, propertyName, propertyName));

        fieldContent.append("""
                    def %s_take(n: Int): %s = {
                        val len = math.min(raw.%sLength, n)
                        var i = 0
                        val buf = ArrayBuffer.empty[%s]
                        while (i < len) {
                            buf += raw.%s(i)
                            i += 1
                        }
                        buf.to%s
                    }
                """.formatted(propertyName, propertyType, propertyName, subType, propertyName, type));

        fieldContent.append("""
                    def %s_drop(n: Int): %s = {
                        var i = n
                        val len = raw.%sLength
                        val buf = ArrayBuffer.empty[%s]
                        while (i < len) {
                            buf += raw.%s(i)
                            i += 1
                        }
                        buf.to%s
                    }
                """.formatted(propertyName, propertyType, propertyName, subType, propertyName, type));

        fieldContent.append("""
                    def %s_slice(from: Int, until: Int): %s = {
                        var i = from
                        val len = math.min(raw.%sLength, until)
                        val buf = ArrayBuffer.empty[%s]
                        while (i < len) {
                            buf += raw.%s(i)
                            i += 1
                        }
                        buf.to%s
                    }
                """.formatted(propertyName, propertyType, propertyName, subType, propertyName, type));
    }
}
